﻿#region

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using MediaSage.Lib.Configuration;
using MediaSage.Lib.Utilities;
using NHibernate;
using NHibernate.Criterion;

#endregion

namespace MediaSage.Lib.Database
{
    #region Enumerations

    public enum EImageFormat
    {
        BMP,
        GIF,
        JPEG,
        PNG,
        TIFF,
        WMF,
        UNKNOWN
    }

    public enum EImageSize
    {
        original,
        cover,
        mid,
        thumb,
        poster,
        profile
    }

    public enum EImageType
    {
        poster,
        backdrop,
        profile,
        banner
    }

    #endregion Enumerations

    public class ImageEntity : DatabaseObject
    {
        private int THUMBNAIL_WIDTH = 64;

        public ImageEntity()
        {
            ID = Guid.NewGuid();
            lockObject = new Object();
        }

        #region Properties

        public virtual string Filename { get; set; }

        public virtual byte[] HashCode { get; set; }

        public virtual int Height { get; set; }

        public virtual Guid ID { get; protected set; }

        public virtual EImageFormat ImageFormat { get; set; }

        public virtual EImageSize ImageSize { get; set; }

        public virtual EImageType ImageType { get; set; }

        public virtual string Thumbnail { get; set; }

        public virtual string URL { get; set; }

        public virtual int Width { get; set; }

        private Object lockObject;

        #endregion Properties

        #region Methods

        // Assumes file needs to be moved to the image dir
        public virtual bool SaveImage(string InputFile, Config config)
        {
            Logger logger = config.Logger;
            logger.Log("Entering Image.SaveImage(" + InputFile + ")");

            if (!File.Exists(InputFile))
            {
                logger.Log(Logger.Levels.WARN, "File: " + InputFile + " not found");
                logger.Log("Exiting Image.SaveImage(" + InputFile + "), rc==false");
                return false;
            }

            Image image = null;
            try
            {
                image = Image.FromFile(InputFile, true);
            }
            catch (OutOfMemoryException OutEx)
            {
                logger.Log(Logger.Levels.ERROR, OutEx.Message);
                logger.Log(Logger.Levels.ERROR, OutEx.StackTrace);
                logger.Log("Exiting Image.SaveImage(" + InputFile + "), rc==false");
                return false;
            }
            catch (ArgumentException ArgEx)
            {
                logger.Log(Logger.Levels.ERROR, ArgEx.Message);
                logger.Log(Logger.Levels.ERROR, ArgEx.StackTrace);
                logger.Log("Exiting Image.SaveImage(" + InputFile + "), rc==false");
                return false;
            }

            logger.Log("Image Loaded");
            if (image == null)
            {
                logger.Log(Logger.Levels.ERROR, "image==null after Image.FromFile(" + InputFile + ",true)");
                logger.Log("Exiting Image.SaveImage(" + InputFile + "), rc==false");
                return false;
            }
            logger.Log("Generating MD5 Hash for " + InputFile);
            HashCode = ImageUtils.GenerateMD5HashFromImage(image);

            logger.Log("MD5 Generated");

            // Check to see if we have this image already
            try
            {
                ISession session = DBUtil.GetSession().SessionFactory.OpenSession();
                using (var tx = session.BeginTransaction())
                {
                    Conjunction conj = Restrictions.Conjunction();
                    conj.Add(Restrictions.Eq("HashCode", HashCode));
                    var ie = session.CreateCriteria(typeof(ImageEntity)).Add(conj).UniqueResult<ImageEntity>();
                    tx.Commit();
                    if (ie != null)
                    {
                        this.Filename = ie.Filename;
                        HashCode = ie.HashCode;
                        Height = ie.Height;
                        ImageFormat = ie.ImageFormat;
                        ImageSize = ie.ImageSize;
                        ImageType = ie.ImageType;
                        Thumbnail = ie.Thumbnail;
                        URL = ie.URL;
                        Width = ie.Width;
                        logger.Log("Image exists in DB already, returning reference");
                        image.Dispose();
                        session.Close();
                        return (true);
                    }
                }
                session.Close();
            }
            catch (Exception e)
            {
                logger.Log(Logger.Levels.ERROR, "Exception: " + e.Message);
            }

            logger.Log("Hash not in DB");

            Height = image.Size.Height;
            Width = image.Size.Width;

            ImageFormat = ImageUtils.GetImageFormat(image);
            logger.Log(String.Format("Image Format: {0}", ImageFormat.ToString()));
            ImageFormat saveToType = image.RawFormat;

            if (config.ConvertImages && (ImageFormat != config.ConvertImageToType))
            {
                logger.Log("Converting image to: " + config.ConvertImageToType.ToString());
                switch (config.ConvertImageToType)
                {
                    case EImageFormat.BMP:
                        saveToType = System.Drawing.Imaging.ImageFormat.Bmp;
                        break;

                    case EImageFormat.GIF:
                        saveToType = System.Drawing.Imaging.ImageFormat.Gif;
                        break;

                    case EImageFormat.JPEG:
                        saveToType = System.Drawing.Imaging.ImageFormat.Jpeg;
                        break;

                    case EImageFormat.PNG:
                        saveToType = System.Drawing.Imaging.ImageFormat.Png;
                        break;

                    case EImageFormat.TIFF:
                        saveToType = System.Drawing.Imaging.ImageFormat.Tiff;
                        break;

                    case EImageFormat.WMF:
                        saveToType = System.Drawing.Imaging.ImageFormat.Wmf;
                        break;
                }
            }

            string Filename = Path.Combine(config.ImageDir,
                                           string.Format("{0}.{1}", Guid.NewGuid().ToString(), ImageFormat.ToString()));
            logger.Log("Saving image to: " + Filename);
            this.Filename = Filename;

            if (config.CreateImageThumbnails)
            {
                string ThumbnailFilename = Path.Combine(config.ImageDir,
                                                        string.Format("{0}.{1}", Guid.NewGuid(), ImageFormat.ToString()));
                logger.Log("Creating thumbnail image: " + ThumbnailFilename);
                float scale = image.Width / THUMBNAIL_WIDTH;
                double height = Math.Round(image.Height / scale);
                if (height < 1)
                {
                    height = 1;
                }
                Image thumbnailImage = image.GetThumbnailImage(THUMBNAIL_WIDTH, (int)height, ThumbnailCallback,
                                                               IntPtr.Zero);
                try
                {
                    thumbnailImage.Save(ThumbnailFilename);
                    Thumbnail = ThumbnailFilename;
                }
                catch (Exception e)
                {
                    logger.Log(Logger.Levels.ERROR, "Exception thrown when creating thumbnail, msg=" + e.Message);
                    Thumbnail = null;
                }
            }
            else
            {
                Thumbnail = null;
            }

            try
            {
                image.Save(Filename, image.RawFormat);
            }
            catch (Exception e)
            {
                logger.Log(Logger.Levels.ERROR, "Error saving image, exception detail: " + e.Message);
                logger.Log("Exiting Image.SaveImage(" + InputFile + "), rc==false");
                image.Dispose();
                return false;
            }

            /*try
            {
                this.Save();
            }
            catch (Exception e)
            {
                logger.Log(Logger.Levels.ERROR, "Exception saving image to DB: " + e.Message);
                logger.Log("Exiting Image.SaveImage(" + InputFile + "), rc==false");
                image.Dispose();
                return false;
            }
            */

            logger.Log("Exiting Image.SaveImage(" + InputFile + "), rc=true");
            image.Dispose();
            return true;
        }

        protected virtual bool ThumbnailCallback()
        {
            return true;
        }

        public override void Save()
        {
            ISession session = DBUtil.GetSession();
            using (session.BeginTransaction())
            {
                var dbCE =
                    session.CreateCriteria(typeof(ImageEntity)).Add(Restrictions.Eq("HashCode", HashCode)).UniqueResult
                        <ImageEntity>();
                if (dbCE != null)
                {
                    Filename = dbCE.Filename;
                    HashCode = dbCE.HashCode;
                    Height = dbCE.Height;
                    ImageFormat = dbCE.ImageFormat;
                    ImageSize = dbCE.ImageSize;
                    ImageType = dbCE.ImageType;
                    Thumbnail = dbCE.Thumbnail;
                    URL = dbCE.URL;
                    Width = dbCE.Width;
                }
            }
            base.Save();
        }

        public override void SaveOrUpdate()
        {
            ISession session = DBUtil.GetSession();
            using (session.BeginTransaction())
            {
                var dbCE =
                    session.CreateCriteria(typeof(ImageEntity)).Add(Restrictions.Eq("HashCode", HashCode)).UniqueResult
                        <ImageEntity>();
                if (dbCE != null)
                {
                    Filename = dbCE.Filename;
                    HashCode = dbCE.HashCode;
                    Height = dbCE.Height;
                    ImageFormat = dbCE.ImageFormat;
                    ImageSize = dbCE.ImageSize;
                    ImageType = dbCE.ImageType;
                    Thumbnail = dbCE.Thumbnail;
                    URL = dbCE.URL;
                    Width = dbCE.Width;
                }
            }
            base.SaveOrUpdate();
        }

        #endregion Methods
    }
}